/**
 * @file Restrict.h
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2020-10-18
 * 
 * @copyright Copyright (c) 2020
 * 
 */
#ifndef _RESTRICT_H_
#define _RESTRICT_H_


#include <vector>
#include <cmath>
namespace MyMultigrid
{
    
    class RestrictOperator
    {
    public:
        /* data */
        std::vector<double> input;
    public:
        RestrictOperator(){};
        RestrictOperator(std::vector<double> v){input = v;};
        ~RestrictOperator(){};
        virtual void do_restrict(int l) = 0;
        virtual void set(std::vector<double> v) = 0;
    };

    class FullWeightingRestrictOperator:public RestrictOperator
    {
        public:
        FullWeightingRestrictOperator(){};
        FullWeightingRestrictOperator(std::vector<double> v)
        {  
           FullWeightingRestrictOperator::input = v;
        };
        void do_restrict(int l)
        {
            int low_h = pow(2,l) + l - 2;
            int high_h = pow(2,l + 1) + l - 2;
            int low_2h = pow(2,l-1) + l - 3;
            int high_2h = low_h - 1;
            input[low_2h] = input[low_h];
            input[high_2h] = input[high_h];
            for(int i = low_2h + 1;i < high_2h;i++)
            {
                int j = i - low_2h;
                input[i] = 0.25 * (input[low_h + 2 * j - 1] + 2 * input[low_h + 2 * j] + input[low_h + 2 * j + 1]);
            }
        }
        void set(std::vector<double> v){FullWeightingRestrictOperator::input = v;};
        
    };

    class InjectionRestrictOperator:public RestrictOperator
    {
        public:
        InjectionRestrictOperator(){};
        InjectionRestrictOperator(std::vector<double> v)
        {
            InjectionRestrictOperator::input = v;
        };
        void do_restrict(int l)
        {
            int low_h = pow(2,l) + l - 2;
            int high_h = pow(2,l + 1) + l - 2;
            int low_2h = pow(2,l-1) + l - 3;
            int high_2h = low_h - 1;
            input[low_2h] = input[low_h];
            input[high_2h] = input[high_h];
            for(int i = low_2h + 1;i < high_2h;i++)
            {
                int j = i - low_2h;
                input[i] = input[low_h + 2 * j];
            }
        }
        void set(std::vector<double> v){InjectionRestrictOperator::input = v;};
    };   



} // namespace MyMultigrid
#else
#endif